package morenoapp.components.thinkgraphv2.components;

import java.awt.Component;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

/**
 *
 * @author dmitry.mamonov
 */
public class CheckBoxListTable extends JTable {
    Handler handler;

    public CheckBoxListTable(Handler handler) {
        this.handler = handler;
        setTableHeader(null);
        setModel(new CheckBoxListTableModel());
        setDefaultRenderer(DataItem.class, new TableCellRenderer() {
            JCheckBox component = new JCheckBox();

            @Override
            public Component getTableCellRendererComponent(JTable table, Object aValue, boolean isSelected, boolean hasFocus, int row, int column) {
                DataItem value = (DataItem) aValue;
                component.setSelected(value.isSelected());
                component.setText(value.getText());
                return component;
            }
        });
        setDefaultEditor(DataItem.class, new TableCellEditor() {
            Set<CellEditorListener> listeners = new HashSet<CellEditorListener>();
            JCheckBox component = new JCheckBox();
            ChangeListener componentChangeLIstener = new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    value.setSelected(component.isSelected());
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            stopCellEditing();
                        }
                    });

                }
            };
            DataItem value = null;

            { // initializer
                component.addChangeListener(componentChangeLIstener);
            }

            @Override
            public Component getTableCellEditorComponent(JTable table, Object aValue, boolean isSelected, int row, int column) {
                value = (DataItem) aValue;
                component.setSelected(!value.isSelected());
                component.setText(value.getText());
                return component;
            }

            @Override
            public Object getCellEditorValue() {
                return value;
            }

            @Override
            public boolean isCellEditable(EventObject anEvent) {
                return true;
            }

            @Override
            public boolean shouldSelectCell(EventObject anEvent) {
                return true;
            }

            @Override
            public boolean stopCellEditing() {
                for (CellEditorListener lst : listeners) {
                    lst.editingStopped(new ChangeEvent(this));
                }
                return true;
            }

            @Override
            public void cancelCellEditing() {
                //as you wish
                for (CellEditorListener lst : listeners) {
                    lst.editingCanceled(new ChangeEvent(this));
                }
            }

            @Override
            public void addCellEditorListener(CellEditorListener l) {
                listeners.add(l);
            }

            @Override
            public void removeCellEditorListener(CellEditorListener l) {
                listeners.remove(l);
            }
        });
    }

    private class CheckBoxListTableModel implements TableModel {
        List<DataItem> dataList = new ArrayList<DataItem>();
        Set<TableModelListener> listeners = new HashSet();

        public CheckBoxListTableModel() {
            refreshData();
        }

        @Override
        public int getRowCount() {
            return dataList.size();
        }

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public String getColumnName(int columnIndex) {
            return "Dummy";
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return DataItem.class;
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return dataList.get(rowIndex);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            dataList.set(rowIndex, (DataItem) aValue);
            handler.applyChanges((DataItem) aValue);
        }

        @Override
        public void addTableModelListener(TableModelListener l) {
            listeners.add(l);
        }

        @Override
        public void removeTableModelListener(TableModelListener l) {
            listeners.remove(l);
        }

        public void refreshData() {
            dataList.clear();
            dataList.addAll(handler.provideData());
            System.out.println("data >> size >> " + dataList.size());
            for (TableModelListener lst : listeners) {
                lst.tableChanged(new TableModelEvent(this));
            }
        }
    }

    public void refreshData() {
        ((CheckBoxListTableModel) getModel()).refreshData();
    }

    public static class DataItem<T> {
        private T id;
        private String text;
        private boolean selected;

        public DataItem(T id, String text, boolean selected) {
            this.id = id;
            this.text = text;
            this.selected = selected;
        }

        public T getId() {
            return id;
        }

        public boolean isSelected() {
            return selected;
        }

        public void setSelected(boolean selected) {
            this.selected = selected;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        @Override
        public String toString() {
            return getText();
        }
    }

    public static interface Handler<T> {
        List<DataItem<T>> provideData();

        void applyChanges(DataItem<T> aItem);
    }
}
